<!doctype html>
<html>
<head>
<title>three-js test</title>
<script src="./three.min.js"></script>
<script>

function ElementManager( scene ) {
  this.elements = [ ] ;
  this._init( scene ) ;
}

ElementManager._ELEMENT_NUM = 100 ;


ElementManager.prototype._init = function( scene ) {
  var xRange = { 'max': 50, 'min': -50 } ;
  var yRange = { 'max': 50, 'min': -50 } ;
  var zRange = { 'max': 50, 'min': -50 } ;
  for( var i = 0; i < ElementManager._ELEMENT_NUM; i++ ) {
    this.elements.push( new Element( scene, xRange, yRange, zRange ) ) ;
  }
} ;


ElementManager.prototype.runStep = function( ) {
  for( var i = 0; i < this.elements.length; i++ ) {
    this.elements[ i ].runStep( ) ;
  }
} ;


ElementManager.prototype.render = function( scene ) {
  for( var i = 0; i < this.elements.length; i++ ) {
    this.elements[ i ].render( scene ) ;
  }
} ;



function Element( scene, xRange, yRange, zRange ) {
  this.x = 0 ;
  this.y = 0 ;
  this.z = 0 ;
  this.xRange = xRange ;
  this.yRange = yRange ;
  this.zRange = zRange ;
  this.view = null ;
  this._initView( scene ) ;
} 

Element._FLAG_MOVE_LEFT  = 0x1 ;
Element._FLAG_MOVE_UP    = 0x2 ;
Element._FLAG_MOVE_RIGHT = 0x3 ;
Element._FLAG_MOVE_DOWN  = 0x4 ;
Element._FLAG_MOVE_NEAR  = 0x5 ;
Element._FLAG_MOVE_FAR   = 0x6 ;


Element.prototype._initView = function( scene ) {
//  var texture = new THREE.ImageUtils.loadTexture( 'marisa_1.png' ) ;
//  var geometry = new THREE.CubeGeometry( 100, 100, 100 ) ;
//  var material = new THREE.MeshLambertMaterial( { map: texture } );
  var geometry = new THREE.CubeGeometry( 1, 1, 1 ) ;
  var material = new THREE.MeshLambertMaterial( { color: 0x0000aa } );
  this.view = new THREE.Mesh( geometry, material );
  scene.add( this.view );
} ;


Element.prototype.render = function( scene ) {
  this.view.position.x = this.x ;
  this.view.position.y = this.y ;
  this.view.position.z = this.z ;
} ;


Element.prototype.runStep = function( ) {
  this._move( ) ;
  this.view.rotation.x += 0.1 ;
  this.view.rotation.y += 0.1 ;
} ;


Element.prototype._move = function( ) {
  switch( this._decideDirection( ) ) {
    case Element._FLAG_MOVE_LEFT:
      this.x-- ;
      break ;
    case Element._FLAG_MOVE_UP:
      this.y-- ;
      break ;
    case Element._FLAG_MOVE_RIGHT:
      this.x++ ;
      break ;
    case Element._FLAG_MOVE_DOWN:
      this.y++ ;
      break ;
    case Element._FLAG_MOVE_NEAR:
      this.z-- ;
      break ;
    case Element._FLAG_MOVE_FAR:
      this.z++ ;
      break ;
    default:
  }
  this._beInTheField( ) ;
} ;


Element.prototype._decideDirection = function( ) {
  return parseInt( Math.random( ) * 6 ) + 1 ;
} ;


Element.prototype._beInTheField = function( ) {
  this.x = this._beInTheRange( this.x, this.xRange ) ;
  this.y = this._beInTheRange( this.y, this.yRange ) ;
  this.z = this._beInTheRange( this.z, this.zRange ) ;
} ;


Element.prototype._beInTheRange = function( value, range ) {
  if( value > range.max )
    value = range.min ;
  if( value < range.min )
    value = range.max ;
  return value ;
} ;


function RandomWalk3D( ) {
  this.flag     = 0 ;
  this.r        = 100 ;
  this.theta    = 0 ;
  this.phi      = 0 ;
  this.scene    = null ;
  this.camera   = null ;
  this.light    = null ;
  this.manager  = null ;
  this.render   = null ;
  this._init( ) ;
}

RandomWalk3D._FLAG_CAMERA_MOVE_LEFT  = 0x1 ;
RandomWalk3D._FLAG_CAMERA_MOVE_UP    = 0x2 ;
RandomWalk3D._FLAG_CAMERA_MOVE_RIGHT = 0x4 ;
RandomWalk3D._FLAG_CAMERA_MOVE_DOWN  = 0x8 ;
RandomWalk3D._FLAG_CAMERA_MOVE_NEAR  = 0x10 ;
RandomWalk3D._FLAG_CAMERA_MOVE_FAR   = 0x20 ;


RandomWalk3D.prototype._init = function( ) {

  this.renderer = new THREE.WebGLRenderer( ) ;
  this.renderer.setSize( 480, 480 ) ;

  this.camera = new THREE.PerspectiveCamera( 45, 480/480, 0.1, 1000 ) ;

  this.light = new THREE.DirectionalLight( 0xffffff, 1.5 ) ;
  this.light.position.set( 300, 300, 0 ) ;

  this.scene = new THREE.Scene( ) ;
  this.scene.add( this.light ) ;

  this.manager = new ElementManager( this.scene ) ;

  document.body.appendChild( this.renderer.domElement );

} ;


RandomWalk3D.prototype.run = function( ) {
  this._update( ) ;
  this._render( ) ;
  requestAnimationFrame( this.run.bind( this ) ) ;
} ;


RandomWalk3D.prototype._update = function( ) {
  this.manager.runStep( ) ;
  this._moveCamera( ) ;
} ;


RandomWalk3D.prototype._moveCamera = function( ) {
  if( this.isFlagSet( RandomWalk3D._FLAG_CAMERA_MOVE_LEFT ) ) {
    this.theta-- ;
  }
/*
  if( this.isFlagSet( RandomWalk3D._FLAG_CAMERA_MOVE_UP ) ) {
    this.phi-- ;
  }
*/
  if( this.isFlagSet( RandomWalk3D._FLAG_CAMERA_MOVE_RIGHT ) ) {
    this.theta++ ;
  }
/*
  if( this.isFlagSet( RandomWalk3D._FLAG_CAMERA_MOVE_DOWN ) ) {
    this.phi++ ;
  }
*/
  if( this.isFlagSet( RandomWalk3D._FLAG_CAMERA_MOVE_NEAR ) ) {
    this.r++ ;
  }
  if( this.isFlagSet( RandomWalk3D._FLAG_CAMERA_MOVE_FAR ) ) {
    this.r-- ;
  }

  this._beCameraInTheField( ) ;

  this.camera.position.x = this.r *
                             Math.sin( this._toRadian( this.theta ) ) *
                             Math.cos( this._toRadian( this.phi ) ) ;
  this.camera.position.y = this.r *
                             Math.sin( this._toRadian( this.theta ) ) *
                             Math.sin( this._toRadian( this.phi ) ) ;
  this.camera.position.z = this.r *
                             Math.cos( this._toRadian( this.theta ) ) ;
  this.camera.lookAt( new THREE.Vector3( 0, 0, 0 ) ) ;
} ;


RandomWalk3D.prototype._beCameraInTheField = function( ) {
  if( this.r < 1 )
    this.r = 1 ;
  if( this.r > 200 )
    this.r = 200 ;
} ;


RandomWalk3D.prototype._render = function( ) {
  this.manager.render( ) ;
  this.renderer.render( this.scene, this.camera ) ;
} ;


RandomWalk3D.prototype.isFlagSet = function( type ) {
  return ( this.flags & type ) ? true : false ;
} ;


RandomWalk3D.prototype.setFlag = function( type ) {
  this.flags |= type ;
} ;


RandomWalk3D.prototype.clearFlag = function( type ) {
  this.flags &= ~type ;
} ;


RandomWalk3D.prototype.handleKeyDown = function( e ) {
  switch( e.keyCode ) {
    case 37: // left
      this.setFlag( RandomWalk3D._FLAG_CAMERA_MOVE_LEFT ) ;
      break ;
    case 38: // up
      this.setFlag( RandomWalk3D._FLAG_CAMERA_MOVE_UP ) ;
      break ;
    case 39: // right
      this.setFlag( RandomWalk3D._FLAG_CAMERA_MOVE_RIGHT ) ;
      break ;
    case 40: // down
      this.setFlag( RandomWalk3D._FLAG_CAMERA_MOVE_DOWN ) ;
      break ;
    case 88: // x
      this.setFlag( RandomWalk3D._FLAG_CAMERA_MOVE_NEAR ) ;
      break ;
    case 90: // z
      this.setFlag( RandomWalk3D._FLAG_CAMERA_MOVE_FAR ) ;
      break ;
  } ;
} ;


RandomWalk3D.prototype.handleKeyUp = function( e ) {
  switch( e.keyCode ) {
    case 37: // left
      this.clearFlag( RandomWalk3D._FLAG_CAMERA_MOVE_LEFT ) ;
      break ;
    case 38: // up
      this.clearFlag( RandomWalk3D._FLAG_CAMERA_MOVE_UP ) ;
      break ;
    case 39: // right
      this.clearFlag( RandomWalk3D._FLAG_CAMERA_MOVE_RIGHT ) ;
      break ;
    case 40: // down
      this.clearFlag( RandomWalk3D._FLAG_CAMERA_MOVE_DOWN ) ;
      break ;
    case 88: // x
      this.clearFlag( RandomWalk3D._FLAG_CAMERA_MOVE_NEAR ) ;
      break ;
    case 90: // z
      this.clearFlag( RandomWalk3D._FLAG_CAMERA_MOVE_FAR ) ;
      break ;
  } ;
} ;


RandomWalk3D.prototype._toRadian = function( theta ) {
  return theta * Math.PI / 180 ;
} ;


RandomWalk3D.prototype._toTheta = function( radian ) {
  return parseInt( radian * 180 / Math.PI ) ;
} ;



var init = function( ) {
  var walk = new RandomWalk3D( ) ;
  window.onkeydown = walk.handleKeyDown.bind( walk ) ;
  window.onkeyup   = walk.handleKeyUp.bind( walk ) ;
  walk.run( ) ;
} ;


</script>
</head>
<body onload='init()'>

<ul>
<li>Left/Right: Rotate Left/Right</li>
<li>z/x: Near/Far</li>
</ul>

</body>
</html>
